home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
libgutil
/
objop.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
16KB
|
817 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
/*
* objop -
* Some operations on geometric objects.
*
* Paul Haeberli - 1987
*/
#include "obj.h"
#include "port.h"
#include "texture.h"
typedef struct hashvert {
struct hashvert *next;
float x, y, z;
float nx, ny, nz;
} hashvert;
static hashvert *newhashvert();
static hashpoint();
static resolvehash();
static setnormal();
static endhash();
static shadeobj();
static swizzle();
static linetouches();
objnormal(obj,nx,ny,nz)
object *obj;
float *nx, *ny, *nz;
{
point *v1, *v2;
float tx, ty, tz;
double Xj, Yj, Zj;
double Xi, Yi, Zi;
float mag;
/* find the normal vector */
v1 = obj->points;
v2 = v1->next;
if(!v2)
return 0;
Xj = v1->x;
Yj = v1->y;
Zj = v1->z;
tx = ty = tz = 0.0;
do {
Xi = Xj; Xj = v2->x;
Yi = Yj; Yj = v2->y;
Zi = Zj; Zj = v2->z;
tx += (Yi - Yj) * (Zi + Zj);
ty += (Zi - Zj) * (Xi + Xj);
tz += (Xi - Xj) * (Yi + Yj);
v1 = v2;
v2 = v2->next;
if(v2 == 0)
v2 = obj->points;
} while( v1 != obj->points );
/* normalize the facet normal */
mag = sqrt(tx*tx + ty*ty + tz*tz);
if( mag < 0.00001 ) {
*nx = 0.0;
*ny = 0.0;
*nz = 0.0;
} else {
*nx = tx/mag;;
*ny = ty/mag;;
*nz = tz/mag;;
}
}
printobj(obj)
object *obj;
{
point *apnt;
while(obj) {
if(obj->points != NULL) {
if(obj->type == OBJ_POINTS) {
printf("type points\n");
while(apnt) {
printf("[%f %f %f] ",apnt->x,apnt->y,apnt->z);
apnt = apnt->next;
}
printf("\n");
} else if(obj->type == OBJ_LINE) {
printf("type line\n");
apnt = obj->points;
while(apnt) {
printf("[%f %f %f] ",apnt->x,apnt->y,apnt->z);
apnt = apnt->next;
}
printf("\n");
printf("\n");
} else if(obj->type == OBJ_LOOP) {
printf("type loop\n");
apnt = obj->points;
while(apnt) {
printf("[%f %f %f] ",apnt->x,apnt->y,apnt->z);
apnt = apnt->next;
}
printf("\n");
printf("\n");
} else if(obj->type == OBJ_POLYGON) {
printf("type poly\n");
apnt = obj->points;
while(apnt) {
printf("[%f %f %f] ",apnt->x,apnt->y,apnt->z);
apnt = apnt->next;
}
printf("\n");
} else
printf("printobj: blat\n");
}
obj = obj->next;
}
}
/*
* Derive normals from geometry.
*
*/
#define HASHSIZE 512
#define HASHFUNC(x,y,z) (((int)((x+y+z) * 30023.32)) & (HASHSIZE-1))
static hashvert *hashtab[HASHSIZE];
static hashvert *newhashvert()
{
return (hashvert *)mymalloc(sizeof(hashvert));
}
/*
* applynormals -
* Put normals onto an object.
*
*/
applynormals(nobj,smoothness)
object *nobj;
float smoothness;
{
object *obj;
point *pnt;
float nx, ny, nz;
if(smoothness < 0.005) {
obj = nobj;
while(obj) {
if(obj->points) {
objnormal(obj,&nx,&ny,&nz);
if(!obj->attr)
obj->attr = newattr();
obj->attr->nx = nx;
obj->attr->ny = ny;
obj->attr->nz = nz;
}
obj = obj->next;
}
} else {
obj = nobj;
/* add all the points to the hash table */
while(obj) {
if(obj->points) {
objnormal(obj,&nx,&ny,&nz);
if(!obj->attr)
obj->attr = newattr();
obj->attr->nx = nx;
obj->attr->ny = ny;
obj->attr->nz = nz;
pnt = obj->points;
while(pnt) {
hashpoint(pnt,obj->attr);
pnt = pnt->next;
}
}
obj = obj->next;
}
/* normalize all the vertex normals */
resolvehash();
/* set the vertex normal based on the smoothness desired */
obj = nobj;
while(obj) {
if(obj->points) {
pnt = obj->points;
while(pnt) {
setnormal(pnt,obj->attr,smoothness);
pnt = pnt->next;
}
}
obj = obj->next;
}
/* free the vert structures */
endhash();
}
}
static hashpoint(pnt,attr)
point *pnt;
attribs *attr;
{
int pos;
hashvert *vptr;
pos = HASHFUNC(pnt->x,pnt->y,pnt->z);
vptr = hashtab[pos];
while(vptr) {
if( (vptr->x == pnt->x) &&
(vptr->y == pnt->y) && (vptr->z == pnt->z) ) {
vptr->nx += attr->nx;
vptr->ny += attr->ny;
vptr->nz += attr->nz;
return;
}
vptr = vptr->next;
}
vptr = newhashvert();
vptr->x = pnt->x;
vptr->y = pnt->y;
vptr->z = pnt->z;
vptr->nx = attr->nx;
vptr->ny = attr->ny;
vptr->nz = attr->nz;
vptr->next = hashtab[pos];
hashtab[pos] = vptr;
}
static resolvehash()
{
int i;
hashvert *vptr;
float mag;
for(i=0; i<HASHSIZE; i++) {
vptr = hashtab[i];
while(vptr) {
mag = sqrt(vptr->nx*vptr->nx+vptr->ny*vptr->ny+vptr->nz*vptr->nz);
vptr->nx /= mag;
vptr->ny /= mag;
vptr->nz /= mag;
vptr = vptr->next;
}
}
}
static setnormal(pnt,attr,smoothness)
point *pnt;
attribs *attr;
float smoothness;
{
int pos;
hashvert *vptr;
float dot;
pos = HASHFUNC(pnt->x,pnt->y,pnt->z);
vptr = hashtab[pos];
while(vptr) {
if( (vptr->x == pnt->x) &&
(vptr->y == pnt->y) && (vptr->z == pnt->z) ) {
if(!pnt->attr)
pnt->attr = newattr();
dot = vptr->nx*attr->nx+vptr->ny*attr->ny+vptr->nz*attr->nz;
if(dot<0.0)
dot = 0.0;
if(dot>(1.0-smoothness)) {
pnt->attr->nx = vptr->nx;
pnt->attr->ny = vptr->ny;
pnt->attr->nz = vptr->nz;
} else {
pnt->attr->nx = attr->nx;
pnt->attr->ny = attr->ny;
pnt->attr->nz = attr->nz;
}
return;
}
vptr = vptr->next;
}
printf("setnormal: bad poop\n");
}
static endhash()
{
int i;
hashvert *vptr, *nptr;
float mag;
for(i=0; i<HASHSIZE; i++) {
vptr = hashtab[i];
while(vptr) {
nptr = vptr->next;
free(vptr);
vptr = nptr;
}
hashtab[i] = 0;
}
}
static float ix, iy, iz;
static TEXTURE *env;
/*
* setlight -
* Set the position of a single light source.
*
*/
setlight(x,y,z)
float x, y, z;
{
float mag;
mag = sqrt(x*x+y*y+z*z);
ix = x/mag;
iy = y/mag;
iz = z/mag;
}
setenviron(envname)
char *envname;
{
env = tmopen(envname);
}
/*
* lightobj -
* Illuminate an object.
*
*/
lightobj(obj)
object *obj;
{
int s;
if(!hasnormals(obj))
applynormals(obj,0.0);
while(obj) {
if(obj->type == OBJ_POLYGON)
shadeobj(obj);
obj=obj->next;
}
}
/*
* vertexlightobj -
* Illuminate an object at verticies.
*
*/
vertexlightobj(obj,smoothness)
object *obj;
float smoothness;
{
point *pnt;
/*if(!hasnormals(obj)) */
applynormals(obj,smoothness);
while(obj) {
if(obj->type == OBJ_POLYGON) {
shadeobj(obj);
pnt = obj->points;
while(pnt) {
shadeattr(pnt->attr);
pnt = pnt->next;
}
}
obj=obj->next;
}
}
static shadeobj(obj)
object *obj;
{
shadeattr(obj->attr);
}
shadeattr(attr)
attribs *attr;
{
float dot;
vect p, c;
if(env) {
p.x = attr->nx;
p.y = attr->ny;
p.z = attr->nz;
lookx(&p);
envsample(env,&p,&c);
dot = c.x;
} else {
dot = ix*attr->nx + iy*attr->ny + iz*attr->nz;
dot = (dot+1.0)/2.0;
}
attr->r = 128+(127*dot);
}
/*
* hasnormals -
* Returns true if normals the object already has normals.
*
*/
hasnormals(obj)
object *obj;
{
if(!obj->attr)
return 0;
else if( (obj->attr->nx == 0.0) &&
(obj->attr->ny == 0.0) && (obj->attr->nz == 0.0) )
return 0;
else
return 1;
}
/*
* triarea -
* Return the area of a triangle of points.
*
*/
float triarea(p1,p2,p3)
point *p1, *p2, *p3;
{
double area;
area = (p1->x-p3->x)*(p1->y+p3->y);
area += (p3->x-p2->x)*(p3->y+p2->y);
area += (p2->x-p1->x)*(p2->y+p1->y);
return area/2.0;
}
#define PREV(p) ((p-1+npoints)%npoints)
#define NEXT(p) ((p+1)%npoints)
#define NOTBAD (-1)
#define XPLANE 1
#define YPLANE 2
#define ZPLANE 3
static swizzle( obj )
object *obj;
{
int sign, dir;
point *pnt;
float temp;
float tx, ty, tz;
/* determine which plane to project the polygon onto */
objnormal(obj,&tx,&ty,&tz);
dir = ZPLANE;
sign = -1;
if( (ABS(tx) > ABS(ty)) && (ABS(tx) > ABS(tz)) ) {
dir = XPLANE;
if(tx>0.0) sign = 1;
} else if( (ABS(ty) > ABS(tx)) && (ABS(ty) > ABS(tz)) ) {
dir = YPLANE;
if(ty>0.0) sign = 1;
} else if( (ABS(tz) > ABS(tx)) && (ABS(tz) > ABS(ty)) ) {
dir = ZPLANE;
if(tz>0.0) sign = 1;
}
/* project the polygon onto the plane */
pnt = obj->points;
switch(dir) {
case XPLANE:
while(pnt) {
temp = pnt->x;
pnt->x = pnt->y;
pnt->y = pnt->z;
pnt->z = temp;
pnt = pnt->next;
}
break;
case YPLANE:
while(pnt) {
temp = pnt->z;
pnt->z = pnt->y;
pnt->y = pnt->x;
pnt->x = temp;
pnt = pnt->next;
}
break;
}
return (sign*dir);
}
unswizzle( obj, dir )
object *obj;
int dir;
{
point *pnt;
float temp;
if(dir<0)
dir = -dir;
/* unproject the polygon from the plane */
pnt = obj->points;
switch(dir) {
case XPLANE:
while(pnt) {
temp = pnt->z;
pnt->z = pnt->y;
pnt->y = pnt->x;
pnt->x = temp;
pnt = pnt->next;
}
break;
case YPLANE:
while(pnt) {
temp = pnt->x;
pnt->x = pnt->y;
pnt->y = pnt->z;
pnt->z = temp;
pnt = pnt->next;
}
break;
}
}
/*
* toconvex -
* Make all the polygons in an object convex.
*
*/
toconvex(obj)
object *obj;
{
object *nextobj, *tobj;
object *obj1, *obj2;
point *pnt, *pntlist, **w;
int i, pos, npoints;
int split, badone, deltasplit;
int s;
while(obj) {
nextobj = obj->next;
if(obj->type == OBJ_POLYGON) {
if(obj->points) {
/* count the points */
npoints = 0;
pnt = obj->points;
while(pnt) {
npoints++;
pnt = pnt->next;
}
if(npoints>3) {
w = (point **)mymalloc((2+npoints)*sizeof(point *));
/* get pointers into a linear array */
i = 0;
pnt = obj->points;
while(pnt) {
w[i++] = pnt;
pnt = pnt->next;
}
w[npoints] = w[0];
w[npoints+1] = w[1];
/* see if this poly is a bad one */
s = swizzle(obj);
badone = NOTBAD;
for(i=0; i<npoints; i++) {
if(!w[i+2]) {
printf("what1\n");
}
if((s*triarea(w[i+0],w[i+1],w[i+2])) > 0.0) {
badone = NEXT(i);
break;
}
}
/*
* find a point that can be seen from the bad point. This is done
* on a cheezy way right now. Make it do binary subdivision
* sometime.
*/
if(badone != NOTBAD) {
split = (badone+(3*npoints/2))%npoints;
deltasplit = 1;
for(pos=3; pos<npoints; pos++) {
/* if the line doesn't touch then split along it */
if(!w[split]) {
printf("what2\n");
printf("npoints %d split is %d\n",npoints,split);
}
if((s*triarea(w[badone],w[badone+1],w[split]))< 0.0 &&
!linetouches(w[badone],w[split],obj)) {
/* unlink into a single ring of verts */
obj->next = 0;
/* arrange the badone to be the first vertex in the list */
if(badone != 0) {
w[badone-1]->next = 0;
w[npoints-1]->next = w[0];
obj->points = w[badone];
}
/* clone the object header, but not the point list. Make obj1 and obj2 */
obj1 = obj;
pntlist = obj1->points;
obj1->points = 0;
obj2 = cloneobj(obj);
obj1->points = pntlist;
/* step on down n points and clone the last point */
pnt = obj1->points;
pntlist = w[split];
w[PREV(split)]->next = clonepnt(w[split]);
/* clone the other point and link in the other part */
obj2->points = clonepnt(obj1->points);
obj2->points->next = pntlist;
/* make the first ring convex */
toconvex(obj1);
/* make the last ring in this list point to object 2 */
for(tobj = obj1; tobj->next; tobj = tobj->next)
;
tobj->next = obj2;
/* make the second ring convex */
toconvex(obj2);
/* make the last ring in this list point to the next object */
for(tobj = obj2; tobj->next; tobj = tobj->next)
;
tobj->next = nextobj;
break;
}
split += deltasplit;
split = (split+npoints)%npoints;
if(deltasplit>0)
deltasplit = -deltasplit-1;
else
deltasplit = -deltasplit+1;
}
if(pos==npoints)
fprintf(stderr,"toconvex: bad poop\n");
}
unswizzle(obj,s);
free(w);
}
}
}
obj = nextobj;
}
}
static linetouches(p1,p2,obj)
point *p1, *p2;
object *obj;
{
point *pnt, *npnt;
float v1, v2;
pnt = obj->points;
npnt = pnt->next;
while(pnt) {
v1 = triarea(p1,p2,pnt) * triarea(p1,p2,npnt);
v2 = triarea(pnt,npnt,p1) * triarea(pnt,npnt,p2);
if((v1<0.0) && (v2<0.0))
return 1;
pnt = pnt->next;
npnt = npnt->next;
if(!npnt)
npnt = obj->points;
}
return 0;
}
#define MAXPOINTS 100
static point **toquadwork;
/*
* toquads -
* Make all the polygons in an object quadralaterals -
* even triangles.
*
*/
toquads(obj)
object *obj;
{
object *nobj, *nextobj;
point *pnt, *fpnt;
point *pntlist;
int i, npoints, npolys;
int left, right;
if(!toquadwork)
toquadwork = (point **)mymalloc(MAXPOINTS*sizeof(point *));
while(obj) {
nextobj = obj->next;
if(obj->type == OBJ_POLYGON) {
if(obj->points) {
npoints = 0;
fpnt = obj->points;
while(fpnt) {
toquadwork[npoints++] = fpnt;
fpnt = fpnt->next;
}
if(npoints>MAXPOINTS) {
fprintf(stderr,"toquads: polygon too big\n");
exit(1);
}
if(npoints != 4) {
if(npoints<3) {
freepnts(obj->points);
obj->points = 0;
} else if(npoints == 3) {
pnt = clonepnt(obj->points);
pnt->next = obj->points;
obj->points = pnt;
} else {
npolys = (npoints-1)/2;
left = 0;
right = 1;
pntlist = obj->points;
obj->points = 0;
if(npoints & 1) {
pnt = clonepnt(toquadwork[right]);
pnt->next = obj->points;
obj->points = pnt;
pnt = clonepnt(toquadwork[right]);
pnt->next = obj->points;
obj->points = pnt;
pnt = clonepnt(toquadwork[left]);
pnt->next = obj->points;
obj->points = pnt;
pnt = clonepnt(toquadwork[PREV(left)]);
pnt->next = obj->points;
obj->points = pnt;
left = PREV(left);
} else {
pnt = clonepnt(toquadwork[NEXT(right)]);
pnt->next = obj->points;
obj->points = pnt;
pnt = clonepnt(toquadwork[right]);
pnt->next = obj->points;
obj->points = pnt;
pnt = clonepnt(toquadwork[left]);
pnt->next = obj->points;
obj->points = pnt;
pnt = clonepnt(toquadwork[PREV(left)]);
pnt->next = obj->points;
obj->points = pnt;
left = PREV(left);
right = NEXT(right);
}
npolys--;
for(i=0; i<npolys; i++) {
nobj = newobj();
nobj->type = OBJ_POLYGON;
if(obj->attr)
nobj->attr = cloneattr(obj->attr);
pnt = clonepnt(toquadwork[NEXT(right)]);
pnt->next = nobj->points;
nobj->points = pnt;
pnt = clonepnt(toquadwork[right]);
pnt->next = nobj->points;
nobj->points = pnt;
pnt = clonepnt(toquadwork[left]);
pnt->next = nobj->points;
nobj->points = pnt;
pnt = clonepnt(toquadwork[PREV(left)]);
pnt->next = nobj->points;
nobj->points = pnt;
nobj->next = obj->next;
obj->next = nobj;
left = PREV(left);
right = NEXT(right);
}
freepnts(pntlist);
}
}
}
}
obj = nextobj;
}
}
lookx(v)
vect *v;
{
float temp;
temp = v->z; /* to make us look down the x axis */
v->z = v->y;
v->y = -v->x;
v->x = temp;
}